package junit.test;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.ConvertUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;

/**
 * 传智播客版的Spring容器
 */
public class ItcastClassPathXMLApplicationContext {
	private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
	private Map<String, Object> sigletons = new HashMap<String, Object>();

	public ItcastClassPathXMLApplicationContext(String filename){
		this.readXML(filename);
		this.instanceBeans();
		this.annotationInject(); //专门处理注解方式
		this.injectObject();
	}
	/**
	 * ͨ��ע��ʵ��ע����5����
	 */
	private void annotationInject() {
		for(String beanName : sigletons.keySet()){//循环所有的bean对象
			Object bean = sigletons.get(beanName);
			if(bean!=null){//获取到bean对象后就判断下bean对象是否存在
				try {
					//对属性进行处理
					PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
					//获取bean的属性
					for(PropertyDescriptor properdesc : ps){//for循环属性描述
						Method setter = properdesc.getWriteMethod();//获取属性的setter方法
						if(setter!=null && setter.isAnnotationPresent(ItcastResource.class)){
							ItcastResource resource = setter.getAnnotation(ItcastResource.class);
							Object value = null;
							if(resource.name()!=null && !"".equals(resource.name())){
								value = sigletons.get(resource.name());
							}else{
								value = sigletons.get(properdesc.getName());
								if(value==null){//当找不到与名称匹配的bean会按类型去寻找
									for(String key : sigletons.keySet()){//for循环所有的bean对象
										if(properdesc.getPropertyType().isAssignableFrom(sigletons.get(key).getClass())){
											//判断属性的类型是否和bean想匹配
											//isAssignableFrom这个方法判断properdesc.getPropertyType()这个类型是否是
											//sigletons.get(key).getClass()的接口或者父类，或者是它类的本身
											value = sigletons.get(key);
											break;
										}
									}
								}
							}
							setter.setAccessible(true);
							setter.invoke(bean, value);//把引用对象注入属性
						}
					}
					//对字段进行处理
					Field[] fields = bean.getClass().getDeclaredFields();
					//找到申明的所有字段
					for(Field field : fields){
						if(field.isAnnotationPresent(ItcastResource.class)){
							ItcastResource resource = field.getAnnotation(ItcastResource.class);
							Object value = null;
							if(resource.name()!=null && !"".equals(resource.name())){
								value = sigletons.get(resource.name());
							}else{
								value = sigletons.get(field.getName());
								if(value==null){
									for(String key : sigletons.keySet()){
										if(field.getType().isAssignableFrom(sigletons.get(key).getClass())){
											value = sigletons.get(key);
											break;
										}
									}
								}
							}
							field.setAccessible(true);//设置允许访问private字段
							field.set(bean, value);
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 为bean对象的属性注入值
	 */
	private void injectObject() {
		for(BeanDefinition beanDefinition : beanDefines){
			Object bean = sigletons.get(beanDefinition.getId());
			if(bean!=null){
				try {
					PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
					//Introspector通过这个类可以取得bean的定义信息
					for(PropertyDefinition propertyDefinition : beanDefinition.getPropertys()){
						for(PropertyDescriptor properdesc : ps){
							if(propertyDefinition.getName().equals(properdesc.getName())){
								Method setter = properdesc.getWriteMethod();// 获取属性的setter方法,private
								if(setter!=null){//属性可能没有set方法，所以这里要判断一下
									Object value = null;
									if(propertyDefinition.getRef()!=null && !"".equals(propertyDefinition.getRef().trim())){
										value = sigletons.get(propertyDefinition.getRef());
									}else{
										value = ConvertUtils.convert(propertyDefinition.getValue(), properdesc.getPropertyType());
										/*
										 我们怎么将本身是字符串的值转成相应的属性类型的值呢？
										 这时我们可以使用一个叫beanutils的工具，是Apache开源组织给我们提供的，使用它的话，可以很容易的
										 把字符串类型的值转换成我需要的属性类型的值
										*/
									}
									setter.setAccessible(true);//如果set方法是私有的话,要设置它允许被访问
									setter.invoke(bean, value);// 把引用对象注入到属性
								}
								break;
							}
						}
					}
				} catch (Exception e) {
				}
			}
		}
	}
	/**
	 * 实现bean的实例化
	 */
	private void instanceBeans() {
		for(BeanDefinition beanDefinition : beanDefines){
			try {
				if(beanDefinition.getClassName()!=null && !"".equals(beanDefinition.getClassName().trim()))
					sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
			} catch (Exception e) {
				// 通过反射技术把bean都创建出来
				e.printStackTrace();
			}
		}

	}
	/**
	 * 读取xml配置文件
	 */
	private void readXML(String filename) {
	       SAXReader saxReader = new SAXReader();
	        Document document=null;
	        try{	        	
	         URL xmlpath = this.getClass().getClassLoader().getResource(filename);
	         document = saxReader.read(xmlpath);
	         Map<String,String> nsMap = new HashMap<String,String>();
	         nsMap.put("ns","http://www.springframework.org/schema/beans");// 加入命名空间
	         XPath xsub = document.createXPath("//ns:beans/ns:bean");// 创建beans/bean查询路径
	         xsub.setNamespaceURIs(nsMap);// 设置命名空间
	         List<Element> beans = xsub.selectNodes(document);// 获取文档下所有bean节点
	         for(Element element: beans){
	            String id = element.attributeValue("id");// 获取id属性值
	            String clazz = element.attributeValue("class"); // 获取class属性值
	            BeanDefinition beanDefine = new BeanDefinition(id, clazz);
	            XPath propertysub =  element.createXPath("ns:property");
	            propertysub.setNamespaceURIs(nsMap);// 设置命名空间
	            List<Element> propertys = propertysub.selectNodes(element);
	            for(Element property : propertys){
	            	String propertyName = property.attributeValue("name");
	            	String propertyref = property.attributeValue("ref");
	            	String propertyValue = property.attributeValue("value");
	            	PropertyDefinition propertyDefinition = new PropertyDefinition(propertyName, propertyref, propertyValue);
	            	beanDefine.getPropertys().add(propertyDefinition);
	            }
	            beanDefines.add(beanDefine);
	         }
	        }catch(Exception e){
	            e.printStackTrace();
	        }
	}
	/**
	 * 获取bean实例
	 */
	public Object getBean(String beanName){
		return this.sigletons.get(beanName);
	}
}
